﻿using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace DesktopToyWpf.Views;

public class MoodProgressBar : Control
{
    private static readonly Type TypeOfThis = typeof(MoodProgressBar);
    private static MoodProgressBar GetView(DependencyObject d) => (MoodProgressBar)d;

    private static void RefreshUICallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        GetView(d).InvalidateVisual();
    }

    #region MainBackgroundProperty

    /// <summary>
    /// 背景刻度颜色
    /// </summary>
    [Category("心情条")]
    public Brush MainBackground
    {
        get => (Brush)GetValue(MainBackgroundProperty);
        set => SetValue(MainBackgroundProperty, value);
    }

    public static readonly DependencyProperty MainBackgroundProperty =
        DependencyProperty.Register(nameof(MainBackground), typeof(Brush), TypeOfThis,
            new PropertyMetadata(Brushes.Black, RefreshUICallback));

    #endregion


    #region PointsProperty

    /// <summary>
    /// 坐标点。X取值[0,24]，Y取值[0,4]，取值可以是小数
    /// </summary>
    [Category("心情条")]
    public ObservableCollection<Point>? Points { set; get; }

    #endregion


    #region PointFillProperty

    /// <summary>
    /// 坐标点填充色
    /// </summary>
    [Category("心情条")]
    public Brush PointFill
    {
        get => (Brush)GetValue(PointFillProperty);
        set => SetValue(PointFillProperty, value);
    }

    public static readonly DependencyProperty PointFillProperty =
        DependencyProperty.Register(nameof(PointFill), typeof(Brush), TypeOfThis,
            new PropertyMetadata(Brushes.Black, RefreshUICallback));

    #endregion


    #region UpAreaFillProperty

    /// <summary>
    /// 上方区域颜色
    /// </summary>
    [Category("心情条")]
    public Brush UpAreaFill
    {
        get => (Brush)GetValue(UpAreaFillProperty);
        set => SetValue(UpAreaFillProperty, value);
    }

    public static readonly DependencyProperty UpAreaFillProperty =
        DependencyProperty.Register(nameof(UpAreaFill), typeof(Brush), TypeOfThis,
            new PropertyMetadata(Brushes.Black, RefreshUICallback));

    #endregion


    #region DownAreaFillProperty

    /// <summary>
    /// 下方区域颜色
    /// </summary>
    [Category("心情条")]
    public Brush DownAreaFill
    {
        get => (Brush)GetValue(DownAreaFillProperty);
        set => SetValue(DownAreaFillProperty, value);
    }

    public static readonly DependencyProperty DownAreaFillProperty =
        DependencyProperty.Register(nameof(DownAreaFill), typeof(Brush), TypeOfThis,
            new PropertyMetadata(Brushes.Black, RefreshUICallback));

    #endregion


    #region StrokeColorProperty

    /// <summary>
    /// 贝塞尔曲线颜色
    /// </summary>
    [Category("心情条")]
    public Brush StrokeColor
    {
        get => (Brush)GetValue(StrokeColorProperty);
        set => SetValue(StrokeColorProperty, value);
    }

    public static readonly DependencyProperty StrokeColorProperty =
        DependencyProperty.Register(nameof(StrokeColor), typeof(Brush), TypeOfThis,
            new PropertyMetadata(Brushes.Black, RefreshUICallback));

    #endregion

    

    
    
    private const int LINE_COUNT = 25; // 刻度线个数

    protected override void OnRender(DrawingContext dc)
    {
        base.OnRender(dc);

        // 画背景刻度
        var pointA = new Point(0, ActualHeight / 2);
        var pointB = new Point(ActualWidth, ActualHeight / 2);
        var length = pointB.X - pointA.X;

        var linePen = new Pen(MainBackground, 2);

        dc.DrawLine(linePen, pointA, pointB);


        for (var i = 0; i < LINE_COUNT; i++)
        {
            var pointUp = new Point(i * length / (LINE_COUNT - 1), 0);
            var pointDown = new Point(i * length / (LINE_COUNT - 1), ActualHeight);
            dc.DrawLine(linePen, pointUp, pointDown);
        }

        if (Points == null || Points.Count == 0) return;
        
        // 画贝塞尔曲线
        if (Points.Count >= 1)
        {
            var drawPoints = Points.Select(ConvertPoint).ToArray();
            DrawUpBezier(dc, drawPoints, UpAreaFill);
            DrawDownBezier(dc, drawPoints, DownAreaFill);
            DrawBezier(dc, drawPoints, new Pen(StrokeColor, 1.5d));
        }

        // 画贝塞尔坐标点
        foreach (var point in Points)
        {
            dc.DrawEllipse(PointFill, null, ConvertPoint(point), 2, 2);
        }
    }

    private Point ConvertPoint(Point point)
    {
        return new Point(point.X * ActualWidth / (LINE_COUNT - 1), (4 - point.Y) * 0.25 * ActualHeight);
    }

    private void DrawUpBezier(DrawingContext dc, Point[] points, Brush fill)
    {
        PathGeometry pathGeometry = new PathGeometry();
        var pathFigure = BuildBezierPath(points, true);

        // 与下方边界行程封闭区域
        pathFigure.Segments.Add(new LineSegment(new Point(ActualWidth, ActualHeight),false));
        pathFigure.Segments.Add(new LineSegment(new Point(0, ActualHeight),false));
        pathFigure.Segments.Add(new LineSegment(new Point(0, points[0].Y),false));
        pathGeometry.Figures.Add(pathFigure);

        dc.PushClip(new RectangleGeometry(new Rect(0, 0, ActualWidth, ActualHeight / 2)));
        dc.DrawGeometry(fill, null, pathGeometry);
        dc.Pop();
    }
    
    private void DrawDownBezier(DrawingContext dc, Point[] points, Brush fill)
    {
        PathGeometry pathGeometry = new PathGeometry();
        var pathFigure = BuildBezierPath(points, true);

        // 与上方边界行程封闭区域
        pathFigure.Segments.Add(new LineSegment(new Point(ActualWidth, 0),false));
        pathFigure.Segments.Add(new LineSegment(new Point(0, 0),false));
        pathFigure.Segments.Add(new LineSegment(new Point(0, points[0].Y),false));
        pathGeometry.Figures.Add(pathFigure);

        dc.PushClip(new RectangleGeometry(new Rect(0, ActualHeight / 2, ActualWidth, ActualHeight / 2)));
        dc.DrawGeometry(fill, null, pathGeometry);
        dc.Pop();
    }
    
    private void DrawBezier(DrawingContext dc, Point[] points, Pen strokePen)
    {
        PathGeometry pathGeometry = new PathGeometry();
        var pathFigure = BuildBezierPath(points, true);

        // 与上方边界行程封闭区域
        pathFigure.Segments.Add(new LineSegment(new Point(points[^1].X, 0),false));
        pathFigure.Segments.Add(new LineSegment(new Point(0, 0),false));
        pathFigure.Segments.Add(new LineSegment(points[0],false));
        pathGeometry.Figures.Add(pathFigure);
        
        dc.DrawGeometry(null, strokePen, pathGeometry);
    }

    private PathFigure BuildBezierPath(Point[] points, bool isClosed)
    {
        PathFigure pathFigure = new PathFigure();
        
        // 左边顶点到第一个点
        pathFigure.StartPoint = new Point(0, points[0].Y);
        pathFigure.Segments.Add(new LineSegment(points[0], false));
        
        // 中间的贝塞尔曲线
        for (var i = 0; i < points.Length - 1; i++)
        {
            var a = points[i];
            var b = points[i + 1];
            var center = new Point((a.X + b.X) / 2, (a.Y + b.Y) / 2);
            var segment = new BezierSegment(new Point(center.X, a.Y), new Point(center.X, b.Y), b, true);
            pathFigure.Segments.Add(segment);
        }
        
        // 右边顶点到最后一个点
        pathFigure.Segments.Add(new LineSegment(new Point(ActualWidth, points[^1].Y), true));
        pathFigure.Segments.Add(new LineSegment(new Point(ActualWidth, points[^1].Y), false));
        
        pathFigure.IsClosed = isClosed;
        return pathFigure;
    }
}